Skip to content

feat(view): unify lockedRepos and preserve empty-repo state#70

Merged
wgordon17 merged 7 commits into
gordon-code:mainfrom
wgordon17:worktree-feat+empty-repo-state
Apr 17, 2026
Merged

feat(view): unify lockedRepos and preserve empty-repo state#70
wgordon17 merged 7 commits into
gordon-code:mainfrom
wgordon17:worktree-feat+empty-repo-state

Conversation

@wgordon17
Copy link
Copy Markdown
Member

Summary

  • Flatten lockedRepos from per-tab object to unified shared array with backward-compatible migration
  • Config-derived pruning preserves expand/lock state for repos with zero items
  • Defense-in-depth: LOCKED_REPOS_CAP, non-object guard, pre-validation cap enforcement

- Flatten lockedRepos from per-tab object to shared z.array(z.string())
- Add migrateLockedRepos() with shape-based detection and lastActiveTab
  dedup precedence for backward-compatible localStorage migration
- Remove tab parameter from lockRepo, unlockRepo, moveLockedRepo,
  pruneLockedRepos; remove LockedReposTab type
- Add configRepoNames memo in DashboardPage (selectedRepos +
  upstreamRepos + monitoredRepos) for config-derived pruning
- Pass configRepoNames to IssuesTab, PullRequestsTab, ActionsTab as
  optional prop with item-derived fallback
- Remove tab prop from RepoLockControls
- Update USER_GUIDE pin state docs from per-tab to cross-tab shared
- Add 11 new tests: 5 migrateLockedRepos unit tests, 6 empty-repo
  state preservation tests across 3 dashboard tab test files
- Guard against non-object JSON.parse results (primitives, arrays,
  null) before casting to Record in loadViewState migration path
- Add LOCKED_REPOS_CAP (50) and .max(200) string constraint for
  defense-in-depth consistency with ignoredItems/trackedItems caps
- Slice migrateLockedRepos result to LOCKED_REPOS_CAP (50) to prevent
  merged per-tab arrays exceeding Zod .max(50) and wiping view state
- Guard lockRepo push with length < LOCKED_REPOS_CAP to prevent
  runtime accumulation beyond schema limit
An oversized lockedRepos array (>50 entries) that bypasses migration
(already in array format) would fail .max(LOCKED_REPOS_CAP) and cause
safeParse to reject the entire ViewState, wiping all user settings.
Truncate before validation to prevent total state loss.
- Test migrateLockedRepos with actions as preferred tab (full ordering)
- Test >50 merged entries capped at LOCKED_REPOS_CAP (regression guard
  for data-loss bug where oversized array fails Zod .max())
- Test lockRepo silently no-ops at cap boundary
- API smoke tests now include Origin header (WAF correctly blocks
  /api/* without Origin for CSRF protection)
- Adds explicit test that bare /api/health is blocked (403)
- Reduces parallel jobs from 10 to 2 with 300ms delay to prevent
  WAF rate-limit timeouts causing flaky false failures
@wgordon17 wgordon17 marked this pull request as ready for review April 17, 2026 16:32
@wgordon17 wgordon17 merged commit 663f727 into gordon-code:main Apr 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant